namespace ActiveRecord

import System
import System.Collections.Generic
import System.Reflection

partial class ActiveRecordBase:

   // static methods
   public static def Create(type as Type, *attrs as (string)) as ActiveRecordBase:
      #activeRecord = getTypeInstance(type) 
      #arNew = ActiveRecordBase(activeRecord)
      ar = Activator.CreateInstance(type) as ActiveRecordBase
      raise NullReferenceException("Create(${type.Name}, ${join(attrs, ',')}) activeRecord is null.") if ar is null
      ar._columns = stringAryToHash(*attrs)
      ar.Save()
      return ar

   public static def DeleteAll(type as Type) as int:
      activeRecord = getTypeInstance(type)
      return ActiveRecordBase.NonQuery("delete from ${activeRecord.TableName}")

   public static def Find(type as Type, *conditions as (Expression)) as ActiveRecordBase:
      #debug "Find(): type was ${activeRecord.GetType().Name}"
      tbl = FindAll(type, *conditions) as ActiveTable
      return tbl.GetValue(0)
   
   public static def FindById(type as Type, id) as ActiveRecordBase:
      activeRecord = getTypeInstance(type)
      #debug "FindById(type:${activeRecord.GetType().Name}, id:${id})"
      return FindAll(type, SQL.Where(activeRecord.PrimaryKey, id))[0]

   public static def FindAll(type as Type, *conditions as (Expression)) as ActiveTable:
      activeRecord = getTypeInstance(type)
      return FindBySql(getSelectCmdFromConditions(activeRecord, *conditions), type)
   
   public static def FindBySql(sql as string) as ActiveTable:
      return FindBySql(sql, null)
   
   public static def FindBySql(sql as string, type as Type) as ActiveTable:
      verifyConnectParams()
      sql = SanitizeSql(sql)
      tbl = ActiveTable()
      debug "FindBySql():  provider: ${_connectionProvider}, sql: ${sql}"
      using conn = DBIndependent.ConnectionFactory.GetDbConnection(_connectionProvider, _connectionString):
         conn.Open()
         cmd = conn.CreateCommand()
         cmd.CommandText = sql
         dr = cmd.ExecuteReader() 
         inheritanceBaseType = getInheritanceBaseType(type)
         while dr.Read():
            cols = {}
            for i in range(0, dr.FieldCount):
               cols[dr.GetName(i)] = dr.GetValue(i)
            
            #debug "type ${type}, inheritanceBaseType: ${inheritanceBaseType}"
            # if subclass of active record , return class of that type
            if inheritanceBaseType == null or not inheritanceBaseType.IsSubclassOf(typeof(ActiveRecordBase)):
               ar = ActiveRecordBase()
            else: # if uses inheritance, return record specified in inheritance column
               inheritColumn = _activeRecordInheritanceColumns[inheritanceBaseType.AssemblyQualifiedName]
               if cols.ContainsKey(inheritColumn):
                  # have to get AssemblyQualifiedName from FullName here
                  inheritanceType = cols[inheritColumn]
                  if _activeRecordTypeNamesShort.ContainsKey(inheritanceType): # may have been saved with invalid type
                     typeAsmName = _activeRecordTypeNamesShort[inheritanceType]
                     ar = Activator.CreateInstance(Type.GetType(typeAsmName)) as ActiveRecordBase
                     goto finished

               ar = Activator.CreateInstance(inheritanceBaseType) as ActiveRecordBase
            
            :finished
            ar._columns = cols
            
            // add this active record to the results array
            tbl.Add(ar)
         
         conn.Close()
      
      return tbl

   public static def NonQuery(sql as string) as int:
      verifyConnectParams()
      sql = SanitizeSql(sql)
      debug "NonQuery():  provider: ${_connectionProvider}, sql: ${sql}"
      iRet as int
      using conn = DBIndependent.ConnectionFactory.GetDbConnection(_connectionProvider, _connectionString):
         conn.Open()
         cmd = conn.CreateCommand()
         cmd.CommandText = sql
         iRet = cmd.ExecuteNonQuery()
         conn.Close()
      
      return iRet
   
   public static def SanitizeSql(sql as string):
      rx1 = /[^a-zA-Z0-9@_'"\\.\\-\\+\\=\\[\\]\\s\\(\\)\\*\\?]/
      return rx1.Replace(sql, string.Empty)

   public static def Save(activeRecord as ActiveRecordBase) as int:
      iRet as int
      # if primary key field non null, existing record - do update
      if activeRecord.HasValueAtColumn(activeRecord.PrimaryKey):
         iRet = NonQuery( getUpdateCmd( activeRecord ) )
      else: # primary key field null, new record - do insert
         # if inheritance, save in base table with type=TypeName
         debug "Save(${activeRecord.TableName}) activeRecord.usesInheritance() - ${activeRecord.usesInheritance()}"
         if activeRecord.usesInheritance():
            
            type = activeRecord.GetType()
            
            baseTypeName = getInheritanceBaseType(type).AssemblyQualifiedName
            inheritColumn = _activeRecordInheritanceColumns[baseTypeName]
            
            if not activeRecord.HasValueAtColumn(inheritColumn): # only overwrite type name if not already set
               if activeRecord._columns.ContainsKey(inheritColumn):
                  activeRecord._columns[inheritColumn] = type.FullName
               else:
                  activeRecord._columns.Add(inheritColumn, type.FullName)
            
            #debug "inheritance Save() - typeName: ${type}, baseTypeName: ${baseTypeName}"
            iRet = NonQuery( getInsertCmd(baseTypeName, activeRecord._columns) )
         else: # doesn't use inheritance
            iRet = NonQuery( getInsertCmd( activeRecord ) )
         
         activeRecord._taintedPK = true

      load(activeRecord)
      
      # update counter caches for belongs to associations
      if iRet == 1:
         activeRecord.updateOwnerCounters(1)
         
      return iRet
